package edu.zju.gis.dbfg.fileserver.util;

import edu.zju.gis.dbfg.common.constant.Seprator;
import edu.zju.gis.dbfg.common.util.ValidateUtils;
import edu.zju.gis.dbfg.fileserver.constant.ConstructedDataFieldType;
import edu.zju.gis.dbfg.fileserver.constant.ConstructedDataType;
import edu.zju.gis.dbfg.fileserver.controller.DataSpatialController;
import edu.zju.gis.dbfg.fileserver.dataLoader.DataLoaderTasks;
import edu.zju.gis.dbfg.fileserver.dataLoader.ESLoader;
import edu.zju.gis.dbfg.fileserver.model.ConstructedDataField;
import edu.zju.gis.dbfg.fileserver.model.ConstructedDataset;
import edu.zju.gis.dbfg.fileserver.model.LoaderMonitor;
import edu.zju.gis.dbfg.fileserver.model.vo.FieldsMap;
import edu.zju.gis.dbfg.fileserver.util.reader.ShpFileReader;
import org.apache.commons.csv.*;

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

import edu.zju.gis.dbfg.fileserver.constant.FieldFormatType;

public class LocalFileIOUtil {

    public static List<Map<String, String>> csvPreview(String path, boolean hasHeader, Integer size) {
        List<Map<String, String>> result = new ArrayList<>();
        CSVParser csvParser = getCSVParser(path, hasHeader);
        Iterator<CSVRecord> records = csvParser.iterator();
        while (records.hasNext() && result.size() < size) {
            CSVRecord record = records.next();
            result.add(record.toMap());
        }
        return result;
    }

    public static String txtPreview(String path) {
        return "preview txt file.";
    }

    public static ConstructedDataset getDatasetFromFile(String path, Boolean hasHeader) throws Exception {
        CSVParser csvParser = getCSVParser(path, hasHeader);
        List<String> headers = csvParser.getHeaderNames();
        Map<String, String> secondLineItems = csvParser.iterator().next().toMap();
        List<ConstructedDataField> fields = new ArrayList<>();
        ConstructedDataType dataType = ConstructedDataType.Table;
        for (int i = 0; i < headers.size(); i++) {
            ConstructedDataField field = new ConstructedDataField();
            field.setName(headers.get(i));
            field.setText(headers.get(i));
            field.setLength(secondLineItems.size());
            if (ValidateUtils.isDecimal(secondLineItems.get(headers.get(i))))
                field.setType(ConstructedDataFieldType.Double.name());
            else
                field.setType(ConstructedDataFieldType.String.name());
            if (DateUtil.isFormatedDate(secondLineItems.get(headers.get(i)))) {
                //TODO ValidateUtils过于强大 需要反向获得 Format 格式，所以限制只匹配yyyy-MM-dd HH:mm:ss
                field.setType(ConstructedDataFieldType.Date.name());
                field.setIsTime(true);
                field.setFormat(DateUtil.FORMAT.toPattern());
            }
            //判断空间类型
            ConstructedDataType fieldsType = guessType(secondLineItems.get(headers.get(i)));
            if (fieldsType != ConstructedDataType.Table) {
                dataType = fieldsType;
                field.setIsGeometry(true);
                field.setFormat(FieldFormatType.WKT_FIELD);
            }
            fields.add(field);
        }
        ConstructedDataset dataset = new ConstructedDataset(fields);
        int startIndex = path.lastIndexOf(File.separator) + 1;
        int endIndex = path.lastIndexOf(".");
        dataset.setName(path.substring(startIndex, endIndex));
        dataset.setDescription("数据描述...");
        dataset.setDatatype(dataType.name());
        if (!dataType.equals(ConstructedDataType.Table)) {
            dataset.setIsspatialdata(true);
        }
        return dataset;
    }


    public static boolean ImportFromLocal(String sourcePath, Boolean hasHead, ConstructedDataset dataset, List<FieldsMap> fieldsMap) throws IOException {
        List<String> Fline = getStrByLine(sourcePath, 1);
        Map<String, Integer> fieldName_Index = new HashMap<>();
        boolean isCsv = !Fline.get(0).contains("\t");
        if (hasHead) {
            List<String> fieldNames = Arrays.asList(Fline.get(0).split(isCsv ? "," : "\t"));
            if (fieldNames.indexOf(DataSpatialController.tag) != -1)
                fieldNames.set(fieldNames.indexOf(DataSpatialController.tag), "shape");
            for (int i = 0; i < fieldsMap.size(); i++) {
                fieldName_Index.put(fieldsMap.get(i).getSource(), fieldNames.indexOf(fieldsMap.get(i).getTarget()));
            }
        } else {
            for (int i = 0; i < fieldsMap.size(); i++) {
                fieldName_Index.put(fieldsMap.get(i).getSource(), Integer.valueOf(fieldsMap.get(i).getTarget()));
            }
        }
        DataLoaderTasks dataLoaderTasks = BeanUtil.getBean(DataLoaderTasks.class);
        ESLoader loader = new ESLoader();
        loader.setFilename(sourcePath);
        loader.setDataset(dataset);
        loader.setHeader(fieldName_Index);
        loader.setHasHeader(hasHead);
        loader.setSeprator(isCsv ? Seprator.COMMA : Seprator.TAB);
        dataLoaderTasks.execute(loader);
        return true;
    }

    public static boolean ImportFromShp(String sourcePath, ConstructedDataset dataset, List<FieldsMap> fieldsMap, LoaderMonitor loaderMonitor) {
        ShpFileReader reader = new ShpFileReader();
        reader.filename(sourcePath);
        reader.init();
        String[] fieldNames = reader.getHeaders();
        reader.close();
        Map<String, Integer> fieldName_Index = new HashMap<>();
        for (int i = 0; i < fieldsMap.size(); i++) {
            for (int f = 0; f < fieldNames.length; f++) {
                if (fieldsMap.get(i).getSource().equals(fieldNames[f])) {
                    fieldName_Index.put(fieldsMap.get(i).getSource(), f);
                    break;
                }
            }
        }
        //ShpReader默认将空间信息放在最后一列
        fieldName_Index.put("shape", fieldNames.length);
        DataLoaderTasks dataLoaderTasks = BeanUtil.getBean(DataLoaderTasks.class);
        ESLoader loader = new ESLoader();
        loader.setFilename(sourcePath);
        loader.setDataset(dataset);
        loader.setHeader(fieldName_Index);
        loader.setMonitor(loaderMonitor);
        dataLoaderTasks.execute(loader);
        return true;
    }

    public static List<String> getStrByLine(String path, Integer size) throws IOException {
        List<String> lines = new ArrayList<>(size == null ? 10 : size);
        File file = new File(path);
        Path dir = file.toPath();
        if (Files.exists(dir))
            Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                    if (dir.getFileName().toString().startsWith("."))
                        return FileVisitResult.SKIP_SUBTREE;
                    else
                        return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    if (!file.getFileName().toString().startsWith(".")) {
                        try (BufferedReader reader = Files.newBufferedReader(file)) {
                            String line;
                            while ((line = reader.readLine()) != null) {
                                lines.add(line);
                                if (size != null && lines.size() >= size)
                                    return FileVisitResult.TERMINATE;
                            }
                        } catch (IOException e) {

                        }
                    }
                    return FileVisitResult.CONTINUE;
                }
            });

        return lines;
    }

    private static CSVParser getCSVParser(String path, boolean hasHeader) {
        try {
            String firstLine = getStrByLine(path, 1).get(0);
            char delimiter = firstLine.contains("\t") ? '\t' : ',';
            String[] headers = getHeader(firstLine, delimiter, hasHeader);
            CSVFormat format = CSVFormat.DEFAULT.withDelimiter(delimiter)
                    .withHeader(headers)
                    .withSkipHeaderRecord(hasHeader);
            Reader reader = new FileReader(path);
            return new CSVParser(reader, format);
        } catch (Exception e) {
            return null;
        }
    }

    private static String[] getHeader(String firstLine, char delimiter, boolean hasHeader) {
        String[] firstLineItems = firstLine.split(String.valueOf(delimiter));
        String[] headers = new String[firstLineItems.length];
        if (hasHeader) {
            headers = firstLineItems;
        } else {
            for (int i = 0; i < headers.length; i++) {
                headers[i] = String.valueOf(i);
            }
        }
        return headers;
    }

    private static ConstructedDataType guessType(String str) {
        if (str.contains("POINT") || str.contains("MULTIPOINT"))
            return ConstructedDataType.Point;
        if (str.contains("LINESTRING") || str.contains("MULTILINESTRING"))
            return ConstructedDataType.LineString;
        if (str.contains("POLYGON") || str.contains("MULTIPOLYGON"))
            return ConstructedDataType.Polygon;
        return ConstructedDataType.Table;
    }
}
